<!-- 图片/视频上传组件 -->
<template>
  <el-upload
    v-model:file-list="fileList"
    list-type="picture-card"
    :before-upload="handleBeforeUpload"
    :http-request="handleUpload"
    :on-success="handleSuccess"
    :on-error="handleError"
    :on-exceed="handleExceed"
    :accept="props.accept"
    :limit="props.limit"
    multiple
  >
    <el-icon><Plus /></el-icon>
    <template #file="{ file }">
      <div style="width: 100%" class="upload-file-item">
        <div v-if="file.status === 'uploading'" class="upload-loading">
          <el-icon class="loading-icon"><Loading /></el-icon>
          <span>上传中...</span>
        </div>
        <template v-else>
          <!-- 视频预览 -->
          <video
            v-if="props.type === 'video' && isVideoFile(file.url)"
            class="el-upload-list__item-thumbnail"
            :src="file.url"
            controls
          ></video>
          <!-- 图片预览 -->
          <img v-else class="el-upload-list__item-thumbnail" :src="file.url" />
          <span class="el-upload-list__item-actions">
            <!-- 预览 -->
            <span @click="handlePreviewItem(file.url)">
              <el-icon><zoom-in /></el-icon>
            </span>
            <!-- 删除 -->
            <span @click="handleRemove(file.url)">
              <el-icon><Delete /></el-icon>
            </span>
          </span>
        </template>
      </div>
    </template>
  </el-upload>

  <!-- 图片预览器 -->
  <el-image-viewer
    v-if="previewVisible && props.type === 'image'"
    :zoom-rate="1.2"
    :initial-index="previewImageIndex"
    :url-list="previewUrlList"
    @close="handlePreviewClose"
  />

  <!-- 视频预览对话框 -->
  <el-dialog
    v-if="videoPreviewVisible"
    v-model="videoPreviewVisible"
    title="视频预览"
    width="70%"
    destroy-on-close
  >
    <video controls autoplay style="width: 100%" :src="videoPreviewUrl"></video>
  </el-dialog>
</template>

<script setup>
import FileAPI from "@/api/file.api";
import { ElMessage, ElDialog } from "element-plus";
import { Plus, ZoomIn, Delete, Loading } from "@element-plus/icons-vue";
import { ref, watch, onMounted, nextTick } from "vue";

const props = defineProps({
  /**
   * 请求携带的额外参数
   */
  data: {
    type: Object,
    default: () => {
      return {};
    },
  },
  /**
   * 上传文件的参数名
   */
  name: {
    type: String,
    default: "file",
  },
  /**
   * 文件上传数量限制
   */
  limit: {
    type: Number,
    default: 10,
  },
  /**
   * 单个文件的最大允许大小
   */
  maxFileSize: {
    type: Number,
    default: 10,
  },
  /**
   * 上传文件类型
   */
  accept: {
    type: String,
    default: "image/*", //  默认支持所有图片格式 ，如果需要指定格式，格式如下：'.png,.jpg,.jpeg,.gif,.bmp'
  },
  /**
   * 文件类型：image 或 video
   */
  type: {
    type: String,
    default: "image", // 默认为图片上传
    validator: (value) => ["image", "video"].includes(value),
  },
});

const previewVisible = ref(false); // 是否显示图片预览
const previewImageIndex = ref(0); // 预览图片的索引
const previewUrlList = ref([]); // 预览图片列表

const videoPreviewVisible = ref(false); // 是否显示视频预览
const videoPreviewUrl = ref(""); // 预览视频URL

const modelValue = defineModel("modelValue", {
  type: String,
  default: () => "[]",
});

const fileList = ref([]);
const parsedData = ref([]);
// 防止重复渲染的锁
const updating = ref(false);

// 判断是否为视频文件
const isVideoFile = (url) => {
  if (!url) return false;
  const videoExtensions = [".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv", ".webm"];
  return videoExtensions.some((ext) => url.toLowerCase().endsWith(ext));
};

// 解析JSON字符串
const parseModelValue = () => {
  try {
    if (!modelValue.value) {
      parsedData.value = [];
      return [];
    }

    const parsed = JSON.parse(modelValue.value);
    if (Array.isArray(parsed)) {
      parsedData.value = parsed;
      return parsed;
    }
    parsedData.value = [];
    return [];
  } catch (error) {
    console.error("解析JSON字符串失败:", error);
    parsedData.value = [];
    return [];
  }
};

// 更新modelValue，但不触发重复渲染
const updateModelValue = () => {
  if (updating.value) return;
  updating.value = true;

  nextTick(() => {
    modelValue.value = JSON.stringify(parsedData.value);
    updating.value = false;
  });
};

// 从parsedData同步到fileList，避免完全重置
const syncFileList = () => {
  const existingFiles = new Set(fileList.value.map((file) => file.url));
  const newFiles = [];

  // 处理已有文件
  parsedData.value.forEach((item) => {
    if (item.url) {
      // 如果文件已在列表中，保留它，不做修改
      if (!existingFiles.has(item.url)) {
        newFiles.push({
          name: item.name || "未命名",
          url: item.url,
          status: "success",
          uid: getUid(),
        });
      }
    }
  });

  // 添加新文件到列表末尾，不重置整个列表
  if (newFiles.length > 0) {
    fileList.value.push(...newFiles);
  }

  // 更新预览列表
  previewUrlList.value = parsedData.value.map((item) => item.url);
};

// 监听modelValue变化，更新显示值
watch(
  modelValue,
  () => {
    if (updating.value) return;
    parseModelValue();

    // 不再一次性重置fileList，而是智能同步
    if (fileList.value.length === 0) {
      // 初始化时完整赋值
      fileList.value = parsedData.value.map((item) => ({
        name: item.name || "未命名",
        url: item.url,
        status: "success",
        uid: getUid(),
      }));
    } else {
      // 后续变更只同步差异部分
      syncFileList();
    }

    previewUrlList.value = parsedData.value.map((item) => item.url);
  },
  { immediate: true }
);

/**
 * 删除文件
 */
function handleRemove(fileUrl) {
  FileAPI.delete(fileUrl).then(() => {
    // 从parsedData中删除
    const index = parsedData.value.findIndex((item) => item.url === fileUrl);
    if (index !== -1) {
      parsedData.value.splice(index, 1);
      updateModelValue();

      // 同步更新fileList - 直接删除对应项，不重置整个列表
      const fileIndex = fileList.value.findIndex((file) => file.url === fileUrl);
      if (fileIndex !== -1) {
        fileList.value.splice(fileIndex, 1);
      }

      // 更新预览列表
      previewUrlList.value = parsedData.value.map((item) => item.url);
    }
  });
}

/**
 * 上传前校验
 */
function handleBeforeUpload(file) {
  // 校验文件类型：虽然 accept 属性限制了用户在文件选择器中可选的文件类型，但仍需在上传时再次校验文件实际类型，确保符合 accept 的规则
  const acceptTypes = props.accept.split(",").map((type) => type.trim());

  // 检查文件格式是否符合 accept
  const isValidType = acceptTypes.some((type) => {
    if (type === "image/*") {
      // 如果是 image/*，检查 MIME 类型是否以 "image/" 开头
      return file.type.startsWith("image/");
    } else if (type === "video/*") {
      // 如果是 video/*，检查 MIME 类型是否以 "video/" 开头
      return file.type.startsWith("video/");
    } else if (type.startsWith(".")) {
      // 如果是扩展名 (.png, .jpg)，检查文件名是否以指定扩展名结尾
      return file.name.toLowerCase().endsWith(type);
    } else {
      // 如果是具体的 MIME 类型 (image/png, image/jpeg)，检查是否完全匹配
      return file.type === type;
    }
  });

  if (!isValidType) {
    ElMessage.warning(`上传文件的格式不正确，仅支持：${props.accept}`);
    return false;
  }

  // 限制文件大小
  if (file.size > props.maxFileSize * 1024 * 1024) {
    ElMessage.warning(
      `上传${props.type === "video" ? "视频" : "图片"}不能大于${props.maxFileSize}M`
    );
    return false;
  }
  return true;
}

/*
 * 上传文件
 */
function handleUpload(options) {
  const file = options.file;

  // 确保文件状态正确设置为 uploading
  const uploadingFile = fileList.value.find((f) => f.uid === file.uid);
  if (uploadingFile) {
    uploadingFile.status = "uploading";
  } else {
    // 手动添加上传文件到列表
    fileList.value.push({
      name: file.name,
      uid: file.uid,
      status: "uploading",
    });
  }

  return new Promise((resolve, reject) => {
    const formData = new FormData();
    formData.append(props.name, file);

    // 处理附加参数
    Object.keys(props.data).forEach((key) => {
      formData.append(key, props.data[key]);
    });

    FileAPI.upload(formData)
      .then((data) => {
        resolve({
          ...data,
          uid: file.uid,
        });
      })
      .catch((error) => {
        // 移除上传失败的文件
        const index = fileList.value.findIndex((f) => f.uid === file.uid);
        if (index !== -1) {
          fileList.value.splice(index, 1);
        }
        reject(error);
      });
  });
}

/**
 * 上传文件超出限制
 */
function handleExceed() {
  ElMessage.warning(`最多只能上传${props.limit}个${props.type === "video" ? "视频" : "图片"}`);
}

/**
 * 上传成功回调
 */
const handleSuccess = (fileInfo, uploadFile) => {
  ElMessage.success("上传成功");

  // 找到当前上传的文件索引
  const index = fileList.value.findIndex((file) => file.uid === uploadFile.uid);
  if (index !== -1) {
    // 更新fileList - 只更新这一项，不重置整个列表
    fileList.value[index].url = fileInfo.url;
    fileList.value[index].status = "success";

    // 添加到parsedData - 直接添加这一项，不触发全列表更新
    const newItem = { name: uploadFile.name || fileInfo.name || "未命名", url: fileInfo.url };
    parsedData.value.push(newItem);

    // 通过 nextTick 延迟更新，避免视图闪烁
    nextTick(() => {
      updateModelValue();

      // 只更新预览列表中新增的项
      if (!previewUrlList.value.includes(fileInfo.url)) {
        previewUrlList.value.push(fileInfo.url);
      }
    });
  }
};

/**
 * 上传失败回调
 */
const handleError = (error) => {
  console.log("handleError");
  ElMessage.error("上传失败: " + error.message);
};

/**
 * 预览文件（图片或视频）
 */
const handlePreviewItem = (fileUrl) => {
  if (props.type === "video" || isVideoFile(fileUrl)) {
    // 视频预览
    videoPreviewUrl.value = fileUrl;
    videoPreviewVisible.value = true;
  } else {
    // 图片预览
    previewImageIndex.value = previewUrlList.value.findIndex((url) => url === fileUrl);
    previewVisible.value = true;
  }
};

/**
 * 关闭预览
 */
const handlePreviewClose = () => {
  previewVisible.value = false;
};

/** 获取一个不重复的id */
function getUid() {
  // 时间戳左移13位（相当于乘以8192） + 4位随机数
  return (Date.now() << 13) | Math.floor(Math.random() * 8192);
}

onMounted(() => {
  parseModelValue();
});
</script>

<style lang="scss" scoped>
.upload-file-item {
  position: relative;
  overflow: hidden;

  .upload-loading {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: rgba(0, 0, 0, 0.5);
    color: #fff;
    z-index: 1;

    .loading-icon {
      animation: loading-rotate 2s linear infinite;
      font-size: 24px;
      margin-bottom: 8px;
    }
  }
}

@keyframes loading-rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

:deep(.el-upload--picture-card) {
  --el-upload-picture-card-size: 148px;
  width: var(--el-upload-picture-card-size);
  height: var(--el-upload-picture-card-size);
  line-height: var(--el-upload-picture-card-size);
}

/* 确保 loading 状态显示 */
:deep(.el-upload-list__item-status-label) {
  display: none !important;
}

:deep(.el-upload-list__item.is-uploading) {
  opacity: 1 !important;
}

:deep(.el-upload-list) {
  transition: none !important;
}

:deep(.el-upload-list__item) {
  transition: none !important;
}
</style>
